In [1]:
import pandas as pd
import altair as alt
In [2]:
ruta = r"C:\Users\maria\Downloads\Python -QLAB\Base de Eventos de Protesta del Perú_1980_2025.xlsx"
data = pd.read_excel(ruta)

data.columns = data.columns.str.lower()

data['número_heridos'] = pd.to_numeric(data['número_heridos'], errors='coerce').fillna(0)
data['número_muertos'] = pd.to_numeric(data['número_muertos'], errors='coerce').fillna(0)

data.head()
Out[2]:
id periódico periódico_id día mes_id mes año fecha presidente_id presidente ... institución_1 institución_2 institución_3 institución_id reclamo_t reclamo reclamo_id sub_reclamo sub_reclamo_id comentar_t
0 1 Expreso 5.0 1 1 Enero 1980 ############ 1 Morales Bermúdez ... Empresas periodísticas Periodicos y diarios. NaN 909.0 Exigen que las empresas periodísticas les otor... Laborales 1 Bonificaciones 105 NaN
1 2 Expreso 5.0 3 1 Enero 1980 ############ 1 Morales Bermúdez ... Instituto Peruano de Seguridad Social NaN NaN 712.0 Demandan una mejora en condiciones salariales. Laborales 1 Aumentos salariales 101 Exigen además: recategorización, aumento gener...
2 3 Expreso 5.0 4 1 Enero 1980 ############ 1 Morales Bermúdez ... Ministerio de Justicia Centro de Readaptación Social de Chorrillos NaN 105.0 Protestan por la mala alimentación recibida en... Servicios 4 Alimentación 407 15 internas resultaron heridas.
3 4 Expreso / El Comercio 6.0 4 1 Enero 1980 ############ 1 Morales Bermúdez ... Empresas periodísticas Empresa Periodistica Nacional S.A. NaN 909.0 Protestan contra medidas de la empresa que ate... Laborales 1 Mejores condiciones laborales 104 Huelga declarada ilegal por el gobierno. Inici...
4 5 El Comercio 7.0 4 1 Enero 1980 ############ 1 Morales Bermúdez ... Municipalidades Provinciales de Cusco Cusco NaN 508.0 Denuncian el despido arbitrario de trabajadores. Laborales 1 Estabilidad laboral 109 También demandan la destitución de cuatro func...

5 rows × 51 columns

In [3]:
variables_interes = ['año', 'región', 'número_heridos', 'número_muertos', 'sector_1']
data_estudio = data[variables_interes].copy()

data_estudio['número_heridos'] = data_estudio['número_heridos'].fillna(0)
data_estudio['número_muertos'] = data_estudio['número_muertos'].fillna(0)
In [4]:
def categorizar_protesta(fila):
    if fila == 0:
        return "Pacífica/Sin víctimas"
    elif fila <= 5:
        return "Violencia Moderada"
    else:
        return "Violencia Alta"
data_estudio['total_victimas'] = data_estudio['número_heridos'] + data_estudio['número_muertos']
data_estudio['intensidad'] = data_estudio['total_victimas'].apply(categorizar_protesta)


print(data_estudio[['total_victimas', 'intensidad']].head())
   total_victimas             intensidad
0             0.0  Pacífica/Sin víctimas
1             0.0  Pacífica/Sin víctimas
2            15.0         Violencia Alta
3             0.0  Pacífica/Sin víctimas
4             0.0  Pacífica/Sin víctimas
In [5]:
grafico_1 = alt.Chart(data_estudio).mark_line(point=True).encode(
    x = alt.X('año:O', title='Año'),
    y = alt.Y('count():Q', title='Cantidad de Protestas'),
    tooltip = ['año', 'count()']
).properties(title='Evolución de las Protestas en el Perú', width=500).interactive()
In [6]:
import altair as alt

alt.data_transformers.disable_max_rows()
Out[6]:
DataTransformerRegistry.enable('default')
In [7]:
grafico_1.display()
In [8]:
top_regiones = data_estudio['región'].value_counts().head(10).index.tolist()
data_top = data_estudio[data_estudio['región'].isin(top_regiones)]

grafico_2 = alt.Chart(data_top).mark_bar().encode(
    x = alt.X('count():Q', title='Número de Eventos'),
    y = alt.Y('región:N', sort='-x', title='Departamento'),
    color = alt.Color('intensidad:N', title='Nivel de Intensidad'),
    tooltip = ['región', 'intensidad', 'count()']
).properties(title='Intensidad de Protestas en Regiones Top', width=500)
In [9]:
grafico_2.display()
In [10]:
variables_interes1 = ['año', 'región', 'periódico', 'número_muertos', 'sector_1']
data_estudio1 = data[variables_interes1].copy()

tabla = pd.crosstab(data_estudio1['periódico'], data_estudio1['sector_1'], normalize='index') * 100
In [11]:
data_periodicos = data_estudio1.assign(periódico=data_estudio1['periódico'].str.split('/'))
data_periodicos = data_periodicos.explode('periódico')
data_periodicos['periódico'] = data_periodicos['periódico'].str.strip()
In [12]:
tabla = pd.crosstab(data_periodicos['sector_1'], data_periodicos['periódico'], normalize='columns') * 100

tabla_altair = tabla.reset_index()
tabla_melted = tabla_altair.melt(id_vars='sector_1', var_name='periódico', value_name='porcentaje')
In [14]:
grafico_final = alt.Chart(tabla_melted).mark_bar().encode(
    x = alt.X('porcentaje:Q', title='Porcentaje de Cobertura (%)'),
    y = alt.Y('sector_1:N', sort='-x', title='Sector de la Protesta'),
    color = alt.Color('periódico:N', title='Diario'),
    tooltip = ['periódico', 'sector_1', 'porcentaje']
).properties(
    title='Agenda Mediática: Distribución de Sectores por Diario (Valores Separados)',
    width=600,
    height=400
).interactive()

grafico_final.display()